/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.worldmodel;

import robocup.component.WorldModel;
import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.ControllerNumbers.REFEREE_MESSAGE;
import robocup.component.infotypes.PlayModeInfo;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.RefereeInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.MessageFactory.MESSAGES;
import robocup.component.tactics.AbstractState.STATES;

/**
 * This Model updates and stores the playmode. In most cases an update is 
 * trivial as the new playmode is diretly communicated by the SServer. 
 * However, sometimes those messages are missed by the player. We try to avoid 
 * these cases by using inderict updates (i.e. a moving ball is only possible
 * in PLAY_MODE==PLAY_ON).
 * 
 */
public class PlayModeModel implements IModel {

	private WorldModel world = null;

	// the last heared refereee message type
	private REFEREE_MESSAGE lastRefereeMessageType = REFEREE_MESSAGE.UNKNOWN;

	// the last heared playmode
	private PLAY_MODE lastPlayModeMessageType = PLAY_MODE.UNKNOWN;

	/**
	 * the current playmode (!= lastHearedPlaymode)
	 * 
	 * @see robocup.component.worldmodel.PlayModeModel.correctPlayMode
	 */
	private PLAY_MODE currentPlayMode = PLAY_MODE.BEFORE_KICK_OFF;

	private int lastRefereeMessageArrivalTime = -1;

	private int lastPlayModeMessageArrivalTime = -1;

  //previous play mode
	private PLAY_MODE previousPlayMode;

  //play mode of the last cycle
	private PLAY_MODE lastCyclePlayMode;

	/**
	 * Constructor.
   * 	
	 * @param world -
	 *            the current world-model
	 */
	public PlayModeModel(WorldModel world) {

		this.world = world;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.SenseBodyInfo)
	 */
	public void update(SenseBodyInfo info) {

		this.lastCyclePlayMode = this.currentPlayMode;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.VisualInfo)
	 */
	public void update(VisualInfo info) {

    //if the player who does the kick off (number 10) didn't here the kick off 
    //command but was able to accelerate we assume the command was sent
		if (this.world.getNumber() == 10
				&& this.world.getPlayMode() == PLAY_MODE.BEFORE_KICK_OFF) {
			if (this.world.getSelfRef().getSpeed().getLength() > 0.3
					&& this.world.getSelfRef().getLastState() == STATES.BEFORE_KICKOFF) {
				if (!PLAY_MODE.META_PENALTY_OWN.contains(world.getPlayMode())
						&& !PLAY_MODE.META_PENALTY_OTHER.contains(world
								.getPlayMode())) {
					System.out
							.println(this.world.getCycle()
									+ " SET PLAY_MODE TO PLAY ON FOR NUMBER 10 (NO PLAY_ON HEARED)");
					this.correctPlayMode(PLAY_MODE.PLAY_ON);
				}
			}
		}

    //if the opponent scored and a teammate tells us he passed the ball we 
    //assume that the PLAY_MODE->PLAY_ON message was overheared.
		if (this.currentPlayMode == PLAY_MODE.GOAL_KICK_OWN
				&& this.world.getSelfRef().getLastHearedPlayer() == 1
				&& this.world.getSelfRef().getLastHearedMessageType() == MESSAGES.KICKED_BALL) {
			if (this.world.getBallRef().getSpeed().getLength() > 0.5)
				this.correctPlayMode(PLAY_MODE.PLAY_ON);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.RefereeInfo)
	 */
	public void update(RefereeInfo info) {

		this.lastRefereeMessageType = info.getRefereeMessageType();
		this.lastRefereeMessageArrivalTime = this.world.getBodyCycle();

    //some referee info always announce the same play mode so we set it here
		switch (info.getRefereeMessageType()) {
		case OFFSIDE_OTHER:
			this.correctPlayMode(PLAY_MODE.FREE_KICK_OWN);
			break;

		case OFFSIDE_OWN:
			this.correctPlayMode(PLAY_MODE.FREE_KICK_OTHER);
			break;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.PlayModeInfo)
	 */
	public void update(PlayModeInfo info) {

		this.previousPlayMode = this.currentPlayMode;
		this.lastPlayModeMessageType = info.getPlayModeType();
		this.currentPlayMode = info.getPlayModeType();
		this.lastPlayModeMessageArrivalTime = this.world.getBodyCycle();

    //if the mode changes to a standard play mode the "kicked standard" flag
    //is set to "false" for all players
		if (info.getPlayModeType() != PLAY_MODE.PLAY_ON) {
			for (int i = 0; i < 22; i++) {
				this.world.getExpectedPlayers()[i].setKickedStandard(false);
				this.world.getAllPlayers()[i].setKickedStandard(false);
				this.world.getNextPlayers()[i].setKickedStandard(false);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see robocup.component.worldmodel.IModel#update(robocup.component.infotypes.PlayerSayInfo)
	 */
	public void update(PlayerSayInfo info) {
	}

	/**
	 * @return Returns the lastPlayModeMessage.
	 */
	public PLAY_MODE getLastPlayModeMessageType() {

		return this.lastPlayModeMessageType;
	}

	/**
	 * @return Returns the lastPlayModeMessageArrivalTime.
	 */
	public int getLastPlayModeMessageCycle() {

		return this.lastPlayModeMessageArrivalTime;
	}

	/**
	 * @return Returns the lastRefereeMessageArrivalTime.
	 */
	public int getLastRefereeMessageArrivalTime() {

		return this.lastRefereeMessageArrivalTime;
	}

	/**
	 * @return Returns the lastRefereeMessageType.
	 */
	public REFEREE_MESSAGE getLastRefereeMessageType() {

		return this.lastRefereeMessageType;
	}

	/**
	 * @return Returns the currentPlayMode.
	 */
	public PLAY_MODE getCurrentPlayMode() {

		return this.currentPlayMode;
	}

	/**
	 * sometimes we miss the play_mode message. this method is used for
	 * correction.
	 * 
	 * i.e. if the ball is moving we have to be in PlayMode = PLAY_ON
	 * 
	 * @param play_mode
	 */
	public void correctPlayMode(PLAY_MODE play_mode) {

		this.currentPlayMode = play_mode;

	}

	/**
	 * @return Returns the previousPlayMode.
	 */
	public PLAY_MODE getPreviousPlayMode() {

		return this.previousPlayMode;
	}

	/**
	 * @return Returns the lastCyclePlayMode.
	 */
	public PLAY_MODE getLastCyclesPlayMode() {

		return this.lastCyclePlayMode;
	}
}
